// -*-mode:c++; coding:utf-8-*-

#ifndef _XBASE_COMPRESSOR_HPP_
#define _XBASE_COMPRESSOR_HPP_

#include <unistd.h>

#include <boost/asio/buffer.hpp>

// contain all supported compressor header file
#include "compressor_quicklz.hpp"
#include "compressor_zlib.hpp"
#include "compressor_minilzo.hpp"
#include "compressor_bmz.hpp"

namespace xbase
{
	namespace compressor
	{
		enum compressor_code
		{
			NONE = 0,	       // no compress
			QUICKLZ = 1,	       // qkLZ
			ZLIB = 2,	       // zlib
			MINILZO = 3,	       // miniLZO
			BMZ = 4		       // BMZ
		};

		inline
		bool valid(compressor_code code) {
			return (code >= NONE) &&
				(code <= BMZ);
		}

		inline
		const char *compressor_name(compressor_code code)
		{
			switch(code)
			{
			case NONE:
				return "none";
				break;
			case QUICKLZ:
				return "quicklz";
				break;
			case ZLIB:
				return "zlib";
				break;
			case MINILZO:
				return "minilzo";
				break;
			case BMZ:
				return "bmz";
				break;
			default:
				return "unknown";
				break;
			}
		}

		/*!
		 * \brief Wrapper for any compressor implementation
		 *
		 * detail comments come here
		 *
		 * example code:
		 * \code
		 *
		 * \endcode
		 */
		template<typename CompressorAlgorithm>
		class compressor
		{
		public:
			typedef CompressorAlgorithm algo_type;

			/*!
			 * \brief Compress data into buffer.
			 * 
			 * \param buffer Dynamic buffer for compressed
			 *               data.
			 * 
			 * \param data Data to be compressed.
			 *
			 * \return Size of compressed data, sure equal
			 *         to buffer.size().
			 */
			template<typename Buffer>
			static inline
			ssize_t compress(Buffer &buffer,
					      const boost::asio::const_buffer &data) {
				return algo_type::compress(buffer, data);
			}

			/*!
			 * \brief Decompress data into buffer
			 *
			 * \param buffer Dynamic buffer for
			 *               decompressed data.
			 *
			 * \param data Data to be decompressed.
			 *
			 * \return Size of decompressed data, sure equal
			 *         to buffer.size().
			 */
			template<typename Buffer>
			static inline
			ssize_t decompress(Buffer &buffer,
				       const boost::asio::const_buffer &data) {
				return algo_type::decompress(buffer, data);
			}
		};

		// also is an example for compressor class
		struct none
		{
			template<typename Buffer>
			static inline
			ssize_t compress(Buffer &buffer,
				     const boost::asio::const_buffer &data) {
				const boost::asio::mutable_buffer buf = buffer.prepare(boost::asio::buffer_size(data));
				std::memcpy(boost::asio::buffer_cast<char*>(buf),
					    boost::asio::buffer_cast<const char*>(data),
					    boost::asio::buffer_size(data));
				buffer.commit(boost::asio::buffer_size(data));
				return boost::asio::buffer_size(data);
			}

			template<typename Buffer>
			static inline
			ssize_t decompress(Buffer &buffer,
				       const boost::asio::const_buffer &data) {
				const boost::asio::mutable_buffer buf = buffer.prepare(boost::asio::buffer_size(data));
				std::memcpy(boost::asio::buffer_cast<char*>(buf),
					    boost::asio::buffer_cast<const char*>(data),
					    boost::asio::buffer_size(data));
				buffer.commit(boost::asio::buffer_size(data));
				return boost::asio::buffer_size(data);
			}
		};

		template<>
		inline
		ssize_t none::decompress(const boost::asio::mutable_buffer &buffer,
				     const boost::asio::const_buffer &data) {
			if(boost::asio::buffer_size(buffer) < boost::asio::buffer_size(data))
			{
				// buffer not enough
				return -1;
			}
			std::memcpy(boost::asio::buffer_cast<char*>(buffer),
				    boost::asio::buffer_cast<const char*>(data),
				    boost::asio::buffer_size(data));
			return boost::asio::buffer_size(data);
		}

		namespace detail
		{
			template<int compressor_code>
			struct compressor_type_selector;

			template<>
			struct compressor_type_selector<NONE>
			{
				typedef none type;
			};

			template<>
			struct compressor_type_selector<QUICKLZ>
			{
				typedef quicklz type;
			};
			
			template<>
			struct compressor_type_selector<ZLIB>
			{
				typedef zlib type;
			};

			template<>
			struct compressor_type_selector<MINILZO>
			{
				typedef minilzo type;
			};

			template<>
			struct compressor_type_selector<BMZ>
			{
				typedef bmz type;
			};
	
		} // namespace detail

		template<typename Buffer>
		inline
		ssize_t compress(const compressor_code &code,
				      Buffer &buffer,
				      const boost::asio::const_buffer &data)
		{
			switch(code)
			{
			case NONE:
				return compressor<detail::compressor_type_selector<NONE>::type>::compress(buffer, data);
			case QUICKLZ:
				return compressor<detail::compressor_type_selector<QUICKLZ>::type>::compress(buffer, data);
			case ZLIB:
				return compressor<detail::compressor_type_selector<ZLIB>::type>::compress(buffer, data);
			case MINILZO:
				return compressor<detail::compressor_type_selector<MINILZO>::type>::compress(buffer, data);
			case BMZ:
				return compressor<detail::compressor_type_selector<BMZ>::type>::compress(buffer, data);
			default:
				return -1;
			};
		}

		template<typename Buffer>
		inline
		ssize_t decompress(const compressor_code &code,
					Buffer &buffer,
					const boost::asio::const_buffer &data)
		{
			switch(code)
			{
			case NONE:
				return compressor<detail::compressor_type_selector<NONE>::type>::decompress(buffer, data);
			case QUICKLZ:
				return compressor<detail::compressor_type_selector<QUICKLZ>::type>::decompress(buffer, data);
			case ZLIB:
				return compressor<detail::compressor_type_selector<ZLIB>::type>::decompress(buffer, data);
			case MINILZO:
				return compressor<detail::compressor_type_selector<MINILZO>::type>::decompress(buffer, data);
			case BMZ:
				return compressor<detail::compressor_type_selector<BMZ>::type>::decompress(buffer, data);
			default:
				return -1;
			};
		}

	} // namespace compressor

} // namespace xbase

#endif	// _XBASE_COMPRESSOR_HPP_
